#include "SGFileSystem.h"

#if defined(_WIN64) || defined(WIN32) || defined(_WIN32)
#include <expected>
#include <windows.h>
#else
#include <limits.h>
#include <unistd.h>
#endif

#include <cmath>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <vector>

#include <boost/filesystem.hpp>
#include <boost/property_tree/ini_parser.hpp>

#include "Utility/Exception/SGException.h"
#include "DataStructure/Common/Validation.h"
// #include "Utility/Logging/SGLogger.h"

using namespace SG::Utility;
using namespace boost::filesystem;

namespace fs = boost::filesystem;

std::string SG::Utility::FileSystem::getTestDataPath (const std::string& relativePath)
{
    fs::path rootPath = fs::current_path ();
    while (true)
    {
        const fs::path vscodeDirPath = rootPath / ".vscode";
        if (fs::exists (vscodeDirPath))
        {
            break;
        }
        rootPath = rootPath.parent_path ();

        // 如果不存在
        if (rootPath.string ().empty ())
        {
            throw SG::Utility::RuntimeError ("未找到项目内.vscosde目录。");
        }
    }

    fs::path fileFullPath = rootPath / "TestData" / relativePath;

    if (!fs::exists (fileFullPath))
    {
        throw SG::Utility::RuntimeError ("未找到测试模型目录。");
    }

    return fileFullPath.make_preferred ().string ();
}

// 封装的函数：从文件路径中提取目录路径和文件名
void SG::Utility::FileSystem::extractDirectoryAndFileName (const char* filePath, std::string& directoryPath, std::string& fileName)
{
    namespace fs = boost::filesystem;
    // 将char*转换为std::string
    std::string strFilePath (filePath);

    // 创建一个path对象
    fs::path const p (strFilePath);

    // 提取目录部分
    directoryPath = p.parent_path ().string ();
    // 提取文件名部分
    fileName = p.filename ().string ();
}

Utility_FileSystem_DECLSPEC std::string SG::Utility::FileSystem::getFullPath (const std::string& relativePath)
{
    fs::path const cwd          = fs::current_path ();
    fs::path       fileFullPath = cwd / relativePath;

    return fileFullPath.make_preferred ().string ();
}

/// @brief 获取当前程序工作路径函数
/// @return
Utility_FileSystem_DECLSPEC std::string SG::Utility::FileSystem::getWorkingDirectory ()

{
    return fs::current_path ().string ();
}

Utility_FileSystem_DECLSPEC bool SG::Utility::FileSystem::compareTextFile (const std::string& filename1, const std::string& filename2)
{
    if (nullptr == freopen (filename1.c_str (), "r", stdin))
    {
        std::cout << "Warning: Can't open " << filename1 << "!\n";
        return false;
    }

    std::string file1Text;
    char        c;
    while (scanf ("%c", &c) != EOF)  // NOLINT(hicpp-vararg)
    {
        file1Text += c;
    }

    assert (EOF != fclose (stdin));

    if (nullptr == freopen (filename2.c_str (), "r", stdin))
    {
        std::cout << "Warning: Can't open " << filename2 << "!\n";
        return false;
    }

    std::string file2Text;
    while (scanf ("%c", &c) != EOF)  // NOLINT(hicpp-vararg)
    {
        file2Text += c;
    }

    assert (EOF != fclose (stdin));

    return file1Text == file2Text;
}

/**
 * @brief 遍历文件夹并获取所有指定后缀的文件路径
 *
 * @param folderPath 要遍历的文件夹路径
 * @param extension  需要匹配的文件后缀（例如 "txt"）
 * @return std::vector<std::string> 包含所有匹配文件绝对路径的向量
 */
Utility_FileSystem_DECLSPEC std::vector<std::string> SG::Utility::FileSystem::getFilesWithExtension (const std::string& folderPath,
                                                                                                     const std::string& extension)
{
    std::vector<std::string> filePaths;  // 用于存储匹配的文件路径

    // 检查输入的路径是否为有效的目录
    if (!fs::exists (folderPath) || !fs::is_directory (folderPath))
    {
        throw FileSystemError ("Invalid directory");
        return filePaths;
    }

    // 递归遍历文件夹中的所有文件和子文件夹
    for (fs::recursive_directory_iterator iter (folderPath), end; iter != end; ++iter)
    {
        // 如果当前路径是一个常规文件并且后缀匹配
        if (fs::is_regular_file (iter->path ()) && iter->path ().extension () == extension)
        {
            // 将绝对路径转换为字符串并添加到结果中
            filePaths.push_back (fs::absolute (iter->path ()).string ());
        }
    }

    return filePaths;
}

Utility_FileSystem_DECLSPEC bool
SG::Utility::FileSystem::compareVTK (const std::string& vtk1, const std::string& vtk2, const double tolerance)
{
    auto convertToDoubleVector = [] (const std::string& str) -> std::vector<double> {
        std::vector<double> result;
        std::stringstream   ss (str);
        double              value;

        while (ss >> value)
        {
            result.push_back (value);
        }
        return result;
    };

    auto compareDoubleVector = [] (const std::vector<double>& vec1, const std::vector<double>& vec2, const double tolerance) -> bool {
        for (size_t i = 0; i < vec1.size (); i++)
        {
            // if (!(std::fabs (val1 - val2) <= tolerance * std::min (std::fabs (val1), std::fabs (val2))))
            // {
            return is_close (vec1[i], vec2[i], tolerance);;
            // }
        }
        return true;
    };

    // 创建ifstream对象并打开两个文件
    std::ifstream file1 (vtk1);
    if (!file1.is_open ())
    {
        throw std::runtime_error ("Failed to open:" + vtk1);
    }

    std::ifstream file2 (vtk2);
    // 检查文件是否成功打开
    if (!file2.is_open ())
    {
        throw std::runtime_error ("Failed to open:" + vtk2);
    }

    // 定义字符串变量存储每行内容
    std::string line1;
    std::string line2;

    // 行号计数器
    int  line_number = 0;
    bool isOK        = true;

    // 使用getline函数逐行读取两个文件，直到任意一个文件结束
    while (std::getline (file1, line1) && std::getline (file2, line2))
    {
        ++line_number;

        // 比较当前行的内容
        if (line1 != line2)
        {
            std::cout << "Difference at line " << line_number << ":\n";
            std::cout << "File 1: " << line1 << "\n";
            std::cout << "File 2: " << line2 << "\n";
            std::cout << "Convert to double compare... " << "\n";
            std::cout << "tolerance: " << tolerance << "\n";

            std::vector<double> vec1 = convertToDoubleVector (line1);
            std::vector<double> vec2 = convertToDoubleVector (line2);

            if (vec1.size () == 0 || vec2.size () == 0 || !compareDoubleVector (vec1, vec2, tolerance))
            {
                std::cout << "Compare failed!\n";
                isOK = false;
                break;
            }
            else
            {
                std::cout << "Compare OK!\n\n";
            }
        }
    }

    // 如果其中一个文件还有剩余行，则输出差异信息
    if (isOK)
    {
        if (!file2.eof ())
            std::getline (file2, line2);

        if (file1.eof () && !file2.eof ())
        {
            std::cout << "File 1 is shorter than File 2.\n";
            isOK = false;
        }
        else if (!file1.eof () && file2.eof ())
        {
            std::cout << "File 2 is shorter than File 1.\n";
            isOK = false;
        }
    }

    // 关闭文件
    file1.close ();
    file2.close ();

    return isOK;
}

Utility_FileSystem_DECLSPEC std::string SG::Utility::FileSystem::getCurrentExecutablePath ()
{
    boost::filesystem::path result;

#ifdef _WIN32
    char buffer[MAX_PATH];
    GetModuleFileName (NULL, buffer, MAX_PATH);
    result = boost::filesystem::path (buffer).parent_path ();
#else
    char          buffer[PATH_MAX];
    const ssize_t len = ::readlink ("/proc/self/exe", buffer, sizeof (buffer) - 1);
    if (len != -1)
    {
        buffer[len] = '\0';
        result      = boost::filesystem::path (buffer).parent_path ();
    }
#endif

    return result.string ();
}